package com.atguigu.rabitmq.consumer.listener04;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Date;

/**
 * 消息可靠型消费
 *
 * @author Taeyang
 * @date 2025/4/8
 */
@Component
public class ConsumerAcknowledgeMode {

    // 暂时屏蔽 @RabbitListener(queues = "direct_queue_01")
    public void consumer01(Message message, Channel channel) {
        byte[] body = message.getBody();
        String msg = new String(body);
        System.out.println("direct_queue_01 ...msg ----> " + msg + " " + new Date());

        // deliveryTag: 是消息的唯一标识符，在消费者处理消息时，用来确认具体哪条消息被处理。
        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        // 注意：manual模式下
        // 需要保证业务代码可能会出现异常的代码被try/catch包裹并且能够正常捕获，并且不能直接抛出。
        // 如果异常直接抛出或者没有正常捕获，异常将直接由重试机制处理，同时由于手动应答代码执行不到，导致MQ接收不到应答没有处理数据（无法直接丢掉或者进入死信队列又或者重新进入信息队列排队）即没有消费数据
        // 需要在手动消费的channel.basicNack(deliveryTag, true, false)中将第三个参数设置为false，才能重启自己配置的重试机制，重试完成之后进行丢掉或者进入死信队列，如果第三个参数设置为true，那么信息在根据重试机制重试完成全部失败之后再次排队并且再次开始新一轮的重试
        try {
            // 处理后续业务
            int a = 10 / 0;
            System.out.println("处理后续业务");

            // 手动应答：如果正常消费，则返回应答ack；如果非正常消费，则根据重试配置发起重试

            // 手动应答（确认已消费）
            // deliveryTag: 是消息的唯一标识符，在消费者处理消息时，用来确认消息的。
            // true: 表示是否批量处理。
            // 如果设置为 true，则表示确认所有小于等于当前 deliveryTag 的消息；
            // 如果设置为 false，则仅确认当前 deliveryTag 对应的消息。
            channel.basicAck(deliveryTag, true);
        } catch (Exception e) { // 注意：此处需要是Exception，能够捕获到除零异常
            e.printStackTrace();

            // 手动应答（确认未消费成功）
            // deliveryTag: 是消息的唯一标识符，在消费者处理消息时，用来确认消息的。
            // 第一个 true: 表示是否批量处理。
            //  - 如果设置为 true，则表示拒绝所有未确认的消息（包括当前消息），
            //  - 如果设置为 false，则表示只拒绝当前的消息。
            // 第二个 true: 表示消息是否应该重新投递给 RabbitMQ 以便重新排队。
            // - 如果设置为 true，则消息重新投递给 RabbitMQ 以便重新排队。
            // - 如果设置为 false，则消息将被丢弃或进入死信队列（如果已配置）。
            try {
                // 这里异常可以抛出也可以try/catch
                // try/catch可能会更好一些，方便写提示或者书写其他业务
                channel.basicNack(deliveryTag, true, false);
            } catch (IOException ex) {
                System.err.println("拒绝消息时发生异常: " + ex.getMessage());
                throw new RuntimeException("消息拒绝失败", ex);
            }
            throw new RuntimeException(e);
        }
    }
}
